<?php


namespace PKFrame\DataHandler;
defined('PATH_PK') or die();

class Arrays
{

    /**
     * 检查变量是否为数组
     * @param $expression - 要检查的变量
     * @return int
     */
    public static function Is($expression): int
    {
        return (!\is_array($expression) || !count($expression)) ? 0 : count($expression);
    }

    /**
     * 判断是否是一维数据
     * @param $expression
     * @return bool
     */
    public static function IsOne($expression): bool
    {
        return count($expression) == count($expression, 1);
    }

    /**
     * 纠正序列化字符串
     * @param null $value
     * @return null|bool
     */
    public static function IsSerialize($value = null): ?bool
    {
        $value = trim($value);
        if (is_null($value)) {
            return false;
        }
        if ('N;' == $value) {
            return true;
        }
        if (!preg_match('/^([adObis]):/', $value, $badions)) {
            return false;
        }
        switch ($badions[1]) {
            case 'a':
            case 'O':
            case 's':
                if (preg_match("/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $value)) {
                    return true;
                }
                break;
            case 'b':
            case 'i':
            case 'd':
                if (preg_match("/^{$badions[1]}:[0-9.E-]+;\$/", $value)) {
                    return true;
                }
                break;
        }
        return false;
    }

    public static function ToModel(array $form, object &$to)
    {
        //利用好反射机制
        $class = new \ReflectionClass($to);
        foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
            if ($key = $property->getName()) {
                $property->setValue($to, array_key_exists($key, $form) ? $form[$key] : null);
            }
        }
    }

    /**
     * 自处理反序列化（优化反序列化处理错误）
     * @param $serial_str
     * @return array
     */
    public static function Unserialize($serial_str): array
    {
        if (empty($serial_str)) {
            return [];
        }
        if (is_array($serial_str)) {
            return $serial_str;
        } elseif (self::isSerialize($serial_str)) {
            $result = unserialize($serial_str);
            return is_array($result) ? $result : [];
        } else {
            return [];
        }
    }

    /**
     * 返回输入数组中某个单一列的值。
     * @param array $array 指定要使用的多维数组（记录集）
     * @param string $column 需要返回值的列
     * @param null $index_key 作为返回数组的索引/键的列
     * @param null $level 原来数据的维数
     * @return array 返回一个数组
     */
    public static function Column($array = [], $column = '', $index_key = null, $level = null): array
    {
        $result = [];
        if (!Arrays::Is($array)) {
            return $result;
        }
        if (!empty($column) && is_null($level)) {
            if (version_compare(phpversion(), '5.5.0', '>=')) {
                $result = array_column($array, $column, $index_key);
            } else {
                $result = self::_column($array, $column, $index_key);
            }
        } elseif (!empty($column) && is_null($index_key) && !is_null($level)) {
            settype($array, 'array');
            !empty($level) ?: $level = self::Level($array);
            if ($level == 1) {
                foreach ($array as $one) {
                    if (is_array($one))
                        $result[@$one[$column]] = $one;
                    else
                        $result[@$one->$column] = $one;
                }
            } elseif ($level == 2) {
                foreach ($array as $one) {
                    if (is_array($one)) {
                        if (false == isset($result[@$one[$column]]))
                            $result[@$one[$column]] = [];
                        $result[@$one[$column]][] = $one;
                    } else {
                        if (false == isset($result[@$one->$column]))
                            $result[@$one->$column] = [];
                        $result[@$one->$column][] = $one;
                    }
                }
            }
        }
        return $result;
    }

    /**
     * 兼容 5.5 以下没有 array_column
     * @param array $array
     * @param $column
     * @param null $index_key
     * @return array
     */
    private static function _column(array $array, $column, $index_key = null): array
    {
        $result = [];
        if (self::Is($array)) {
            foreach ($array as $arr) {
                if (!is_array($arr)) {
                    continue;
                }
                $value = is_null($column) || !array_key_exists($column, $arr) ? $arr : $arr[$column];
                is_null($index_key) || !array_key_exists($index_key, $arr) ? $result[] = $value : $result[$arr[$index_key]] = $value;
            }
        }
        return $result;
    }

    public static function GetKey($key, array $array = [], $tips = null, $module = null)
    {
        try {
            if (array_key_exists($key, $array)) {
                if (!is_null($tips) && empty($array[$key])) {
                    throw new \Exception(language($tips, $module));
                }
                return $array[$key];
            } elseif (!is_null($tips)) {
                throw new \Exception(language($tips, $module));
            }
        } catch (\Exception $e) {
            request()->isAjax() ? out()->noticeByJson($e->getMessage()) : out()->notice($e->getMessage());
        }
        return null;
    }

    /**
     * 根据某一特定键(下标)取出一维或多维数组的所有值；不用循环的理由是考虑大数组的效率，
     * 把数组序列化，然后根据序列化结构的特点提取需要的字符串
     * @param array $array 被处理的数组
     * @param string $string 下标
     * @return array
     */
    public static function GetArrayByKey(array $array = [], $string = ''): array
    {
        if (!trim($string)) {
            return [];
        }
        preg_match_all("/\"$string\";\w{1}:(?:\d+:|)(.*?);/", serialize($array), $res);
        // 去除多余的引号
        if (isset($res[1]) && is_array($res[1])) {
            foreach ($res[1] as $key => $value) {
                $res[1][$key] = str_replace('"', null, $value);
            }
        }
        return $res[1];
    }

    /**
     * 将 item 转为 key
     * @param array $array
     * @param string|null $item_key
     * @return array
     */
    public static function ItemToKey(array $array, string $item_key = null): array
    {
        $result = [];
        if (self::Is($array)) {
            foreach ($array as $item) {
                $result[is_null($item_key) ? $item : $item[$item_key]] = $item;
            }
        }
        return $result;
    }

    /**
     * 返回数组的维度
     * @param $arr
     * @return mixed
     */
    public static function Level($arr = [])
    {
        $al = array(0);
        function aL($arr, &$al, $level = 0)
        {
            if (is_array($arr)) {
                $level++;
                $al[] = $level;
                foreach ($arr as $v) {
                    aL($v, $al, $level);
                }
            }
        }

        aL($arr, $al);
        return max($al);
    }

    /**
     * 将 Object 转换成 Array
     * @param null $object_str 对象字符
     * @return array|mixed|string|null
     */
    public static function ObjectToArray($object_str = null)
    {
        if (is_object($object_str)) {
            $object_str = get_object_vars($object_str);
        }
        if (is_string($object_str)) {
            return $object_str;
        }
        if (is_array($object_str)) {
            foreach ($object_str as $key => $value) {
                $object_str[$key] = self::objectToArray($value);
            }
        }
        return $object_str;
    }

    /**
     * 将数据转换为连接字符串，同 URL 传参
     * 示例 ： a=1&b=1…
     * @param array $array
     * @return string
     */
    public static function ToQuery(array $array = []): string
    {
        if (is_array($array)) {
            $tmp_array = [];
            foreach ($array as $key => $value) {
                $tmp_array[] = $key . '=' . urlencode($value);
            }
            return implode('&', $tmp_array);
        }
        return '';
    }

    /**
     * 数组转换成可以保存的数组字符
     * @param array $var 数组
     * @param boolean $isSoft 是否排序
     * @return string
     */
    public static function ToSaveString($var = [], $isSoft = TRUE): ?string
    {
        if (is_array($var)) {
            $string_start = "<?php\nreturn\t";
            if ($isSoft) {
                ksort($var);
            }
            $string_process = var_export($var, TRUE);
            $string_end = ";";
            return $string_start . $string_process . $string_end;
        }
        return null;
    }

    /**
     * Xml 转 数组, 包括根键，忽略空元素和属性，尚有重大错误
     * @param null $xml
     * @return array
     */
    public static function xmlToArray($xml = null)
    {
        $reg = "/<(\\w+)[^>]*?>([\\x00-\\xFF]*?)<\\/\\1>/";
        if (preg_match_all($reg, $xml, $matches)) {
            $count = count($matches[0]);
            $arr = [];
            for ($i = 0; $i < $count; $i++) {
                $key = $matches[1][$i];
                $val = self::xmlToArray($matches[2][$i]);  // 递归
                if (array_key_exists($key, $arr)) {
                    if (is_array($arr[$key])) {
                        if (!array_key_exists(0, $arr[$key])) {
                            $arr[$key] = array($arr[$key]);
                        }
                    } else {
                        $arr[$key] = array($arr[$key]);
                    }
                    $arr[$key][] = $val;
                } else {
                    $arr[$key] = $val;
                }
            }
            return $arr;
        } else {
            return $xml;
        }
    }

    /**
     * 简单的递归
     * @param $array
     * @param string $fieldName
     * @param string $parentFieldName
     * @param int $parentId
     * @return array
     */
    public static function Recursion($array, $fieldName = '', $parentFieldName = '', $parentId = 0): array
    {
        $arr = [];
        foreach ($array as $v) {
            if ($v[$parentFieldName] == $parentId) {
                $temp = self::recursion($array, $fieldName, $parentFieldName, $v[$fieldName]);
                if ($temp) {
                    $v['child'] = $temp;
                }
                $arr[] = $v;
            }
        }
        return $arr;
    }

    /**
     * 无限分级, 判断不同级的数据结构
     * @access  public
     * @param array &$data 数据库里取得的结果集 地址引用
     * @param integer $pid 父级id的值
     * @param string $col_id 自增id字段名（对应&$data里的字段名）
     * @param string $col_pid 父级字段名（对应&$data里的字段名）
     * @param string $col_cid 是否存在子级字段名（对应&$data里的字段名）
     * @return array  返回整理好的数组
     */
    public static function RecursionPlus(array &$data, $pid = 0, $col_id = 'id', $col_pid = 'parent', $col_cid = 'hasChild'): array
    {
        $childs = self::findChild($data, $pid, $col_pid);
        if (empty($childs)) {
            return [];
        }
        foreach ($childs as $key => $val) {
            if (isset($val[$col_cid]) && !empty($val[$col_cid])) {
                $treeList = self::recursionPlus($data, $val[$col_id], $col_id, $col_pid, $col_cid);
                if ($treeList !== null) {
                    $childs[$key]['child'] = $treeList;
                }
            }
        }
        return $childs;
    }

    /**
     * 多维数组的递归
     * @param array $array 数组
     * @param int $pid 父级id的值
     * @param string $col_pid 自增id字段名
     * @return array
     */
    private static function findChild(&$array = [], $pid = 0, $col_pid = 'parent'): array
    {
        $rootList = [];
        foreach ($array as $key => $val) {
            if ($val[$col_pid] == $pid) {
                $rootList[] = $val;
                unset($array[$key]);
            }
        }
        return $rootList;
    }
}
